#pragma once

#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>

const int N = 5;
template <class T>
class ringQueue
{
private:
    void P(sem_t &s) { sem_wait(&s); }
    void V(sem_t &s) { sem_post(&s); }
    void lock(pthread_mutex_t &m) { pthread_mutex_lock(&m); }
    void unlock(pthread_mutex_t &m) { pthread_mutex_unlock(&m); }

public:
    ringQueue(int cap = N)
        : _cap(cap), _ring(cap)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, _cap);
        _c_step = _p_step = 0;

        pthread_mutex_init(&_cmtx, nullptr);
        pthread_mutex_init(&_pmtx, nullptr);
    }

    void push(const T &in)
    {
        // 先申请信号量再进行申请锁操作，这样比先申请锁再申请信号效率要高
        // 信号量是临界资源的一种预定机制，在临界区外部就可以进行申请，因为这个操作原子的，不会引发并发访问的安全问题
        P(_space_sem); // 申请空间资源,如果申请失败的话，该线程会被阻塞起来。
        lock(_pmtx);

        //走到这里就说明，信号量一定申请成功了，不用再做判断。开始生产数据
        _ring[_p_step++] = in;
        _p_step %= _cap;//环形

        unlock(_pmtx);
        V(_data_sem);//数据信号量加1
    }

    void pop(T *out)
    {
        P(_data_sem);
        lock(_cmtx);

        *out = _ring[_c_step++];
        _c_step %= _cap;

        unlock(_cmtx);
        V(_space_sem);
    }

    ~ringQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);

        pthread_mutex_destroy(&_cmtx);
        pthread_mutex_destroy(&_pmtx);

    }
private:
    std::vector<T> _ring;
    int _cap; // 环形队列的大小

    sem_t _data_sem;  // 数据资源信号量
    sem_t _space_sem; // 空间资源信号量

    int _c_step; // 消费者位置
    int _p_step; // 生产者位置

    // 为了维护生产和生产以及消费和消费之间的互斥问题，我们就需要两把锁
    pthread_mutex_t _cmtx; // 消费锁
    pthread_mutex_t _pmtx; // 生产锁
};